package com.yungu.swift.schedule.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.yungu.swift.assets.passenger.service.PassengerService;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.common.service.CommonConfigService;
import com.yungu.swift.order.model.dto.OrderDto;
import com.yungu.swift.order.service.OrderService;
import com.yungu.swift.system.marketing.model.dto.MarketingGiftDto;
import com.yungu.swift.system.marketing.model.dto.MarketingGiftWillDrawDto;
import com.yungu.swift.system.marketing.service.MarketingGiftService;
import com.yungu.swift.system.marketing.service.MarketingGiftWillDrawService;
import com.yungu.swift.utils.MapUtils;
import com.yungu.swift.utils.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author Zerrion
 * @version 1.0
 * @description 领取礼包相关定时任务
 * @date 2017/9/26 16:02
 */
@Service
public class DrawGiftService {

    private static final Logger logger = LoggerFactory.getLogger(DrawGiftService.class);

    @Reference
    private PassengerService passengerService;
    @Reference
    private MarketingGiftService marketingGiftService;
    @Reference
    private MarketingGiftWillDrawService marketingGiftWillDrawService;
    @Reference
    private OrderService orderService;
    @Reference
    private CommonConfigService commonConfigService;

    /**
     * 统计沉睡用户并插入到待领取礼包表中
     * <p>
     * 沉睡用户定义：距离上次叫车后，15天内未使用的用户。 可以在每天凌晨0点判断 15天内没有完成订单 并且15天内没有领取该礼包的用户
     * 加上判断15天内没有领取该礼包的条件是因为15天轮询一次 没15天没有订单的就规定为沉睡用户 获得礼包之后 重新计时
     * <p>
     * 每日1点统计
     */
    public void countSleepPassenger() {
        logger.info("====统计沉睡用户定时器开始=====");
        // 获取沉睡用户礼包
        MarketingGiftDto marketingGiftDto = getGiftDto(3);
        if (marketingGiftDto == null) {
            logger.info("====沉睡用户礼包未开启或不存在=====");
            return;
        }
        // 获取到沉睡用户 已过滤15天内获取过沉睡礼包的用户
        List<String> passengerUuids = passengerService.getSleepPassengerUuids(marketingGiftDto.getUnusedDays()).getData();
        List<MarketingGiftWillDrawDto> list = new ArrayList<>();
        //加入到待领取记录表中
        for (String passengerUuid : passengerUuids) {
            list.add(setDefaultGiftWillDrawDto(3, passengerUuid));
        }
        if (CollectionUtils.isNotEmpty(list)) {
            //加入到待领取列表中
            marketingGiftWillDrawService.addBatch(list);
        }
        logger.info("====统计沉睡用户定时器结束=====");
    }

    private MarketingGiftDto getGiftDto(int getMode) {
        Map<String, Object> giftParams = MapUtils.build(8);
        giftParams.put("type", 5);
        giftParams.put("getMode", getMode);
        giftParams.put("status", 1);
        // 限制查询剩余数量大于0
        giftParams.put("hasRemain", 1);
        return marketingGiftService.get(giftParams).getData();
    }

    /**
     * 发放沉睡用户礼包
     * <p>
     * 查询出待领取列表里面 get_mode为 3 status为0的用户 给他们发放沉睡用户礼包 同时将待领取记录中的status设置为1
     * <p>
     * 每日中午12点发放
     */
    public void sendSleepPassengerGifts() {
        logger.info("====发放沉睡用户礼包定时器开始=====");
        // 查询待领取沉睡礼包的用户
        Map<String, Object> params = MapUtils.build(4);
        params.put("getMode", 3);
        params.put("status", 0);
        ResponseData<List<MarketingGiftWillDrawDto>> responseData = marketingGiftWillDrawService.list(params);
        if (responseData.isSuccess() && CollectionUtils.isNotEmpty(responseData.getData())) {
            List<MarketingGiftWillDrawDto> list = responseData.getData();
            List<String> passengerUuids = list.stream().parallel().map(giftWillDrawDto -> giftWillDrawDto.getPassengerUuid())
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(passengerUuids)) {
                doSendGiftAndSendMsg(passengerUuids, 3);
            }
        }
        logger.info("====发放沉睡用户礼包定时器结束=====");
    }

    /**
     * 统计高频用户并插入到待领取礼包表中
     * <p>
     * 高频用户定义：上周使用次数满4次（有效订单）的用户。因为统计是在周一凌晨 所以可以使用条件是当前时间之前7天内使用次数满4次
     * <p>
     * 每周一1点统计
     */
    public void countHighFrequencyPassenger() {
        logger.info("====统计高频用户定时器开始=====");
        // 获取高频用户礼包
        MarketingGiftDto marketingGiftDto = getGiftDto(4);
        if (marketingGiftDto == null) {
            logger.info("====沉睡用户礼包未开启或不存在=====");
            return;
        }
        ResponseData<List<String>> responseData = passengerService.getHighFrequencyPassengerUuids(
                marketingGiftDto.getHighFrequencyDays(), marketingGiftDto.getHighFrequencyNum());
        if (responseData.isSuccess() && CollectionUtils.isNotEmpty(responseData.getData())) {
            List<MarketingGiftWillDrawDto> list = new ArrayList<>();
            //加入到待领取记录表中
            for (String passengerUuid : responseData.getData()) {
                list.add(setDefaultGiftWillDrawDto(4, passengerUuid));
            }
            if (!list.isEmpty()) {
                //加入到待领取列表中
                marketingGiftWillDrawService.addBatch(list);
            }
        }
        logger.info("====统计高频用户定时器结束=====");
    }

    /**
     * 发放高频用户礼包
     * <p>
     * 查询出待领取列表里面 get_mode为4 status为0的用户 给他们发放高频用户礼包 同时将待领取记录中的status设置为 1
     * <p>
     * 每周一12点发放
     */
    public void sendHighFrequencyPassengerGifts() {
        logger.info("====发放高频用户礼包定时器开始=====");
        // 查询待领取高频礼包的用户
        Map<String, Object> params = MapUtils.build(3);
        params.put("getMode", 4);
        params.put("status", 0);
        ResponseData<List<MarketingGiftWillDrawDto>> responseData = marketingGiftWillDrawService.list(params);
        if (responseData.isSuccess() && CollectionUtils.isNotEmpty(responseData.getData())) {
            List<MarketingGiftWillDrawDto> list = responseData.getData();
            List<String> passengerUuids = list.stream().parallel().map(giftWillDrawDto -> giftWillDrawDto.getPassengerUuid())
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(passengerUuids)) {
                doSendGiftAndSendMsg(passengerUuids, 4);
            }
        }
        logger.info("====发放高频用户礼包定时器结束=====");
    }

    /**
     * 统计连续叫单不成功用户并插入到待领取礼包表中
     * <p>
     * 连续叫单不成功用户定义是根据设置自定义的，所以需要先把礼包查询出来查看规则.因为规则自定义 所以需要每天都统计
     * <p>
     * 每天1点统计
     */
    public void countAlwaysNotSuccessOrderPassenger() {
        logger.info("====统计连续叫单不成功用户定时器开始=====");
        // 获取连续叫单不成功礼包
        MarketingGiftDto marketingGiftDto = getGiftDto(5);
        if (marketingGiftDto == null) {
            logger.info("====连续叫单不成功礼包未开启或不存在=====");
            return;
        }
        // 限定时间（分钟）
        Integer limitTime = marketingGiftDto.getLimitTime(),
                // 限定连续叫单不成功次数
                limitCount = marketingGiftDto.getLimitCount(),
                // 限定等待时间（秒）
                limitWait = marketingGiftDto.getLimitWait();
        // 获取3天内有取消订单的乘客的所有订单，并且按乘客和创建时间排序
        List<OrderDto> orderDtoList = orderService.getCancelPassengerOrder(marketingGiftDto.getAppid()).getData();
        // 第一个用于比较的订单，用来判断限定时间
        OrderDto firstContainOrder = null;
        // 当前乘客取消次数，每次取消的等待时间大于限定等待时间 则加1，当取消次数大于限定等待次数时 说明乘客符合连续叫单不成功用户规则
        int passengerCancelCount = 0;
        List<String> passengerUuids = new ArrayList<String>();
        List<OrderDto> passengerOrderList;
        for (int i = 0, len = orderDtoList.size(); i < len; i++) {
            OrderDto orderDto = orderDtoList.get(i);
            // 如果当前订单的乘客已在待领取界面中 则继续下一次循环
            if (passengerUuids.contains(orderDto.getPassengerUuid())) {
                continue;
            }
            passengerOrderList = new ArrayList<>();
            int j = 1;
            OrderDto nextOrderDto = orderDto;
            // 判断下下下下次订单与当前订单不为同一个乘客 或者 超过了限制分钟数的话 则不再循环添加乘客订单
            while (orderDto.getPassengerUuid().equals(nextOrderDto.getPassengerUuid())
                    && (orderDto.getCreateOn().getTime() - nextOrderDto.getCreateOn().getTime()) <= limitTime * 60 * 1000) {
                // 找出该乘客限定时间内的所有订单
                passengerOrderList.add(nextOrderDto);
                if (i + j >= len) {
                    break;
                }
                nextOrderDto = orderDtoList.get(i + j);
                j++;
            }
            for (OrderDto passengerOrder : passengerOrderList) {
                // 过滤出订单状态为取消的
                if (orderDto.getMainStatus() != 5) {
                    firstContainOrder = null;
                    passengerCancelCount = 0;
                    continue;
                } else {
                    // 判断当前订单与上一个订单是否同一个乘客 上一个比较订单为null的话 则说明上一个订单和当前订单不是同一个乘客
                    if (firstContainOrder == null || !firstContainOrder.getPassengerUuid().equals(passengerOrder.getPassengerUuid())) {
                        firstContainOrder = passengerOrder;
                        passengerCancelCount = 0;
                    }
                    // 判断等待时间有没有超过设置的限定等待时长
                    Date cancelTime = passengerOrder.getCancelTime();
                    long waitSecond = 0;
                    if (cancelTime != null) {
                        waitSecond = (cancelTime.getTime() - passengerOrder.getCreateOn().getTime()) / 1000;
                    }
                    // 符合等待订单数加1
                    if (waitSecond >= limitWait) {
                        passengerCancelCount += 1;
                        // 符合发放规则
                        if (passengerCancelCount == limitCount) {
                            passengerUuids.add(orderDto.getPassengerUuid());
                            // 重置初始数据
                            firstContainOrder = null;
                            passengerCancelCount = 0;
                            // 找出符合条件的之后 跳出循环，查找下一个司机
                            break;
                        }
                    }
                }
            }
            firstContainOrder = null;
            passengerCancelCount = 0;
        }
        List<MarketingGiftWillDrawDto> list = new ArrayList<>();
        //加入到待领取记录表中
        for (String passengerUuid : passengerUuids) {
            MarketingGiftWillDrawDto marketingGiftWillDrawDto = setDefaultGiftWillDrawDto(5, passengerUuid);
            list.add(marketingGiftWillDrawDto);
        }
        if (!list.isEmpty()) {
            //加入到待领取列表中
            marketingGiftWillDrawService.addBatch(list);
        }
        logger.info("====统计连续叫单不成功用户定时器结束=====");
    }

    /**
     * 发放连续叫单不成功用户礼包
     * <p>
     * 查询出待领取列表里面get_mode为5 status为 0 的用户 给他们发放连续叫单不成功礼包 同时将待领取记录中的status设置为1
     * <p>
     * 每天12点发放
     */
    public void sendAlwaysNotSuccessOrderPassengerGifts() {
        logger.info("====发放连续叫单不成功用户礼包定时器开始=====");
        // 查询待领取连续叫单不成功礼包的用户
        Map params = MapUtils.build(2);
        params.put("getMode", 5);
        params.put("status", 0);
        ResponseData<List<MarketingGiftWillDrawDto>> responseData = marketingGiftWillDrawService.list(params);
        if (responseData.isSuccess() && CollectionUtils.isNotEmpty(responseData.getData())) {
            List<MarketingGiftWillDrawDto> list = responseData.getData();
            List<String> passengerUuids = list.stream().parallel().map(giftWillDrawDto -> giftWillDrawDto.getPassengerUuid())
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(passengerUuids)) {
                doSendGiftAndSendMsg(passengerUuids, 5);
            }
        }
        logger.info("====发放连续叫单不成功用户礼包定时器结束=====");
    }

    /**
     * 其余值都为默认 所以这里封装一下 代码简洁一点
     *
     * @param getMode       Integer
     * @param passengerUuid
     * @return MarketingGiftWillDrawDto
     */
    private MarketingGiftWillDrawDto setDefaultGiftWillDrawDto(Integer getMode, String passengerUuid) {
        MarketingGiftWillDrawDto marketingGiftWillDrawDto = new MarketingGiftWillDrawDto();
        marketingGiftWillDrawDto.setUuid(StringUtils.buildUUID());
        marketingGiftWillDrawDto.setPassengerUuid(passengerUuid);
        marketingGiftWillDrawDto.setCreateOn(new Date());
        marketingGiftWillDrawDto.setCreateBy("SCHEDULE");
        //沉睡用户或者高频用户或者连续叫单不成功用户
        marketingGiftWillDrawDto.setGetMode(getMode);
        //默认未领取
        marketingGiftWillDrawDto.setStatus(0);
        return marketingGiftWillDrawDto;
    }

    /**
     * 发送礼包操作以及发送短信操作 另外更新已经领取礼包的状态
     *
     * @param passengerUuids List<String>
     * @param getMode        Integer
     */
    public void doSendGiftAndSendMsg(List<String> passengerUuids, Integer getMode) {
        marketingGiftService.sendAppointGift(passengerUuids, getMode);
    }
}
